package test;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import model.Actor;
import model.Animal;
import model.Field;
import model.Fox;
import model.Hunter;
import model.Location;
import model.Rabbit;
import model.Simulator;
import model.Wolf;

import org.junit.Before;
import org.junit.Test;

/**
 * J-Unit tester class
 * @author Arjan Frans, Alwin Rombout, Jelte Verbree, Brendan Kanters
 * @version 1.0
 */
public class tester {
	private ArrayList<Actor> newAnimals;
	private Field field;
	private Simulator simulator;
	
	/**
	 * create the dummy field
	 */
	@Before
	public void setUp() {
		//dummy field, later replaced with the simulator field
		//in the simulator itself
		field = new Field(2,2);
	}
	

	
	/**
	 * Test if a rabbit will breed
	 */
	@Test
	public void testRabbitBreeding()
	{
		System.out.println("Test breeding of rabbits");
		//3x3 field
		int arraySize = 9;
		//Make a location for the rabbit, this location should allow the rabbit to breed 4 little rabbits
		//because of the free space around it
		Location location1 = new Location(1,1);
		
		//create the rabbit
		Rabbit rabbit1 = new Rabbit(false,field,location1);
		//to make sure they breed, set the breeding probability to 100%;
		rabbit1.setBreedingProbability(1);
		
		//Place a fox and a rabbit next to each other on the field
		newAnimals = new ArrayList<Actor>();
		newAnimals.add(rabbit1);
		simulator = new Simulator(newAnimals, 3, 3);
		
		Actor[] animal = new Animal[arraySize];
		//no step has been taken, the rabbit should be there;
		animal = getActors(simulator, arraySize);
		assertTrue(animal[0] instanceof Rabbit);
		//the other locations should be null:
		for(int i=1;i<9;i++) {
			assertNull(animal[i]);
		}
		//the rabbits should not be able to breed, because he is not 5 years old yet but 0
		assertEquals(0, ((Animal) animal[0]).getAge());
		assertFalse(((Animal) animal[0]).canBreed());
		
		//simulate 5 steps so that the rabbit will be able to breed
		simulator.simulateNoStop(5);
		animal = getActors(simulator, arraySize);
		
		//check if there are any new rabbits and if there is a rabbit that can breed (this rabbit shoud be the one that we started with)
		boolean breedingRabbit = false;
		int rabbitCount = 0;
		for(int i=0;i<arraySize;i++){	
			System.out.println("Step " + i);
	  		if(animal[i] instanceof Rabbit)
	  		{
	  			if(((Animal) animal[i]).canBreed()) breedingRabbit = true;
	  			System.out.println("Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());	  			
	  			rabbitCount++;
	  		} 
	  		else {
	  			System.out.println("Location " + i + ": No animal");		  			
	  		}	  					
		}
		//There should be more than one rabbits now
		//Most of the time it will be five but it could be less because there might be overpopulation
		//which results in death of some rabbits
		assertTrue(rabbitCount > 1);		
	}
	
	
	/**
	 * Test a small field with a rabbit and fox next to each other
	 * After one step of simulating the only animal left should be
	 * the fox
	 */
	@Test
	public void testSmallFieldWithFoxAndRabbit()
	{
		System.out.println("Test if a fox eats the rabbit on the field");
		//size of array with animals 
		int arraySize = 4;
		//Make locations for the animals
		Location location1 = new Location(0,0);
	 	Location location2 = new Location(0,1);
		
		//create the animals
		Fox fox1 = new Fox(false, field, location1);
		Rabbit rabbit1 = new Rabbit(false, field, location2);
				
		//Place a fox and a rabbit next to each other on the field
		newAnimals = new ArrayList<Actor>();
		newAnimals.add(fox1);
		newAnimals.add(rabbit1);
		simulator = new Simulator(newAnimals, 2, 2);
		
		Actor[] animal = new Animal[arraySize];
			
		//get a list of the animals on the field
		animal = getActors(simulator, arraySize);
	    
	    //No step has been taken, so both animals should be there
	    assertTrue(animal[0] instanceof Fox);
	    assertTrue(animal[1] instanceof Rabbit);
	    assertNull(animal[2]);
	    assertNull(animal[3]);
	    //take one step
	    simulator.simulateOneStep();	    
	    //get a list of the animals on the field    
	  	animal = getActors(simulator, arraySize);
	  	
	  	//check if animal[0] is a fox and see if the food value is still 7
	  	//the food level should be 7 and not 6 because the fox ate the rabbit
	  	if(animal[0] instanceof Fox) 
	  	{
	  		assertTrue(animal[0] instanceof Fox);
	  		Fox fox = (Fox) animal[0];
	  		assertEquals(7,fox.getFoodLevel());
	  	}
	  	else {
	  		fail("Animal is not a fox");
	  	}	
	  	
	    //The only animals left should be the fox
	  	int foxCount = 0;
	  	int rabbitCount = 0;
	  	//Check the locations for foxes or rabbit
	  	for(int i=0;i<arraySize;i++){	
	  		if(animal[i] instanceof Fox)
	  		{
	  			System.out.println("Fox: Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
	  			foxCount++;
	  		}
	  		else if(animal[i] instanceof Rabbit)
	  		{
	  			System.out.println("Rabbit: Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
	  			rabbitCount++;
	  		}	
	  		else {
	  			System.out.println("Location " + i + ": No animal" );
	  		}
	  	}
	  	//There should only be one fox (not more because his starting age was 0 and he cannot breed after one step) left
	  	//and no rabbits.
	    assertEquals(1, foxCount);
	    assertEquals(0, rabbitCount);
	}
	
	/**
	 * Test to see if a fox dies if it gets no food
	 */
	@Test
	public void testDeadFoxes()
	{
		System.out.println("Test if a fox dies at the age of 7");
		//size of array with animals 
		int arraySize = 4;
				
		//Make locations for the animals
		Location location1 = new Location(0,0);
		
		//create the animals, without a random age!
		Fox fox1 = new Fox(false, field, location1);
		
		//to make sure they breed set the breeding probability to 100%
		fox1.setBreedingProbability(1);
		
		//Place a fox and a rabbit next to each other on the field
		newAnimals = new ArrayList<Actor>();
		newAnimals.add(fox1);
		Actor[] animal = new Animal[arraySize];
		simulator = new Simulator(newAnimals, 2, 2);
		
		//The fox is below the age of 10 so he shouldn't be able to breed
		//Because there are no rabbit the fox will get no food and should die after 7 steps
		animal = getActors(simulator, arraySize);
		
		int foxCount = 0;
		//Simulate 6 steps, the fox should survive
		for(int j=0;j < 6;j++) {
			simulator.simulateOneStep();
			animal = getActors(simulator, arraySize);
			System.out.println("Step " + j);
			foxCount = 0;
			for(int i=0;i<arraySize;i++){				
		  		if(animal[i] instanceof Fox)
		  		{
		  			System.out.println("Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
		  			foxCount++;
		  		} 
		  		else {
		  			System.out.println("Location " + i + ": No animal");		  			
		  		}
		  		
		  	}
		}
		assertEquals(1,foxCount);
		
		//Simulate one more step
		simulator.simulateOneStep();
		animal = getActors(simulator, arraySize);
		foxCount = 0;
		for(int i=0;i<arraySize;i++){				
	  		if(animal[i] instanceof Fox)
	  		{
	  			System.out.println("Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
	  			foxCount++;
	  		} 
	  		else {
	  			System.out.println("Location " + i + ": No animal");
	  		}	  		
	  	}
		//The fox should be dead
		assertEquals(0,foxCount);		
	}
	
	/**
	 * Test a small field with a wolf and fox next to each other
	 * After one step of simulating the only animal left should be
	 * the wolf
	 */
	@Test
	public void testSmallFieldWithWolfAndFox()
	{
		System.out.println("Test if a wolf eats the fox on the field");
		//size of array with animals 
		int arraySize = 4;
		//Make locations for the animals
		Location location1 = new Location(0,0);
	 	Location location2 = new Location(0,1);
		
		//create the animals
		Wolf wolf1 = new Wolf(false, field, location1);
		Fox fox1 = new Fox(false, field, location2);
				
		//Place a fox and a rabbit next to each other on the field
		newAnimals = new ArrayList<Actor>();
		newAnimals.add(wolf1);
		newAnimals.add(fox1);
		simulator = new Simulator(newAnimals, 2, 2);
		
		Actor[] animal = new Animal[arraySize];
			
		//get a list of the animals on the field
		animal = getActors(simulator, arraySize);
	    
	    //No step has been taken, so both animals should be there
	    assertTrue(animal[0] instanceof Wolf);
	    assertTrue(animal[1] instanceof Fox);
	    assertNull(animal[2]);
	    assertNull(animal[3]);
	    //take one step
	    simulator.simulateOneStep();	    
	    //get a list of the animals on the field    
	  	animal = getActors(simulator, arraySize);
	  	
	  	//check if animal[0] is a wolf and see if the food value is still 10
	  	//the food level should be 10 and not 9 because the wolf ate the fox
	  	if(animal[0] instanceof Wolf) 
	  	{
	  		assertTrue(animal[0] instanceof Wolf);
	  		Wolf wolf = (Wolf) animal[0];
	  		assertEquals(10,wolf.getFoodLevel());
	  	}
	  	else {
	  		fail("Animal is not a wolf");
	  	}	
	  	
	    //The only animals left should be the fox
	  	int wolfCount = 0;
	  	int foxCount = 0;
	  	//Check the locations for foxes or rabbit
	  	for(int i=0;i<arraySize;i++){	
	  		if(animal[i] instanceof Wolf)
	  		{
	  			System.out.println("Wolf: Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
	  			wolfCount++;
	  		}
	  		else if(animal[i] instanceof Fox)
	  		{
	  			System.out.println("Fox: Location " + i + ": " + ((Animal) animal[i]).getAge() + "; breed?" + ((Animal) animal[i]).canBreed() + "; alive?" + animal[i].isAlive());
	  			foxCount++;
	  		}	
	  		else {
	  			System.out.println("Location " + i + ": No animal" );
	  		}
	  	}
	  	//There should only be one fox (not more because his starting age was 0 and he cannot breed after one step) left
	  	//and no rabbits.
	    assertEquals(1, wolfCount);
	    assertEquals(0, foxCount);
	}
	
	/**
	 * Test if a hunter shoots a wolf
	 */
	@Test
	public void testShootingHunter()
	{
		System.out.println("Test if a hunter shoots a wolf");
		//size of array with actors 
		int arraySize = 4;
		//Make locations for the animals
		Location location1 = new Location(0,0);
	 	Location location2 = new Location(0,1);
		
		//create the animals
		Wolf wolf1 = new Wolf(false, field, location1);
		Hunter hunter1 = new Hunter(field, location2);
				
		//Place a fox and a hunter next to each other on the field
		ArrayList<Actor> newActors = new ArrayList<Actor>();
		newActors.add(wolf1);
		newActors.add(hunter1);
		simulator = new Simulator(newActors, 2, 2);
		
		Actor[] actors = new Actor[arraySize];
			
		//get a list of the animals on the field
		actors = getActors(simulator, arraySize);
	    
	    //No step has been taken, so both actors should be there
	    assertTrue(actors[0] instanceof Wolf);
	    assertTrue(actors[1] instanceof Hunter);
	    assertNull(actors[2]);
	    assertNull(actors[3]);
	    //take 9 steps, so the chance that the hunter killed the fox is almost 100%
	    //since the shooting direction of the hunter is random there is no guarantee that the fox is killed	    
	    simulator.simulateNoStop(9);	    
	    //get a list of the actors on the field    
	    actors = getActors(simulator, arraySize);
	    
	    //Only the hunter should be left
	  	assertTrue(actors[0] instanceof Hunter);
	  	assertNull(actors[1]);
	  	assertNull(actors[2]);
	  	assertNull(actors[3]);
	 
	}
	
	/**
	 * Method to get the animals from the simulator
	 * @param sim
	 * @return array with animals
	 */
	public Actor[] getActors(Simulator sim, int arraySize)
	{
		//Create a new empty array
		Actor[] animal = new Actor[arraySize];
		ArrayList<Actor> simulatorAnimals = sim.getActors();
		Iterator<Actor> it = simulatorAnimals.iterator();		
		int i = 0;
	    while(it.hasNext()) {
			animal[i] = it.next();
			i++;
		}
	    i = 0;
		return animal;
	}
	/**
	 * Test a small field with a wolf and fox next to each other
	 * After one step of simulating the only animal left should be
	 * the wolf
	 */
	@Test
	public void testSmallFieldVirusInfection()
	{
		System.out.println("Test if one rabbit infects another");
		Animal.setInfection(1.0);
		Animal.setIncubation(2);
		Rabbit.setMaxAge(10);
		Rabbit.setBreedingAge(12);
		Rabbit.setBreedingProbability(0);
		Rabbit.setMaxLitterSize(0);
		Rabbit.setGrassFoodValue(10);
		//size of array with animals 
		int arraySize = 4;
		//Make locations for the animals
		Location location1 = new Location(0,0);
	 	Location location2 = new Location(0,1);
		
		//create the animals
		Rabbit rabbit1 = new Rabbit(false, field, location1);
		Rabbit rabbit2 = new Rabbit(false, field, location2);
		rabbit1.setZiekteGen(true);
		assertTrue(rabbit1.getZiekteGen());
		
				
		//Place a fox and a rabbit next to each other on the field
		newAnimals = new ArrayList<Actor>();
		newAnimals.add(rabbit1);
		newAnimals.add(rabbit2);
		simulator = new Simulator(newAnimals, 2, 2);
		
		Actor[] animal = new Animal[arraySize];
			
		//get a list of the animals on the field
		animal = getActors(simulator, arraySize);
	    
		
		
		
		
	    //No step has been taken, so both animals should be there
	    assertTrue(animal[0] instanceof Rabbit);
	    assertTrue(animal[1] instanceof Rabbit);
	    assertNull(animal[2]);
	    assertNull(animal[3]);
	    Rabbit trabbit1 = (Rabbit) animal[0];
	  	Rabbit trabbit2 = (Rabbit) animal[1];
	  	assertTrue(rabbit1.getZiekteGen());
	  	//assertTrue(trabbit1.getZiekteGen());
	  	//assertFalse(trabbit2.getZiekteGen());
	    //take one step
	    simulator.simulateOneStep();	    
	    //get a list of the animals on the field    
	  	animal = getActors(simulator, arraySize);
	  	
	  	
	  	assertTrue(rabbit1.getZiekteGen());
	  	//assertTrue(rabbit2.getZiekteGen());
	  	
	  	
	  			
	  	
	  	//check if the animals are a rabbit and see if the animal is infected
	  	for(int i = 0; i < 3; i++){
	  		if(animal[i] instanceof Rabbit) 
	  		{
	  			assertTrue(animal[i] instanceof Rabbit);
	  			Rabbit rabbit = (Rabbit) animal[i];
	  		//	assertTrue(rabbit.getZiekteGen());
	  		}
	  		/*else {
	  			fail("Animal is not a rabbit");
	  		}
	  		*/	
	  	}
	  	
	  	
	    
	  	
	  	//run simulator passed the incubation period
	  	simulator.runSimulation(2);
	  //There should not be any rabbits left
	  	int rabbitCount = 0;
	  	//Check the locations for rabbit
	  	for(int i=0;i<arraySize;i++){	
	  		if(animal[i] instanceof Rabbit)
	  		{
	  			System.out.println("Rabbit: Location " + i + ": " + ((Animal) animal[i]).getAge() + "; Virus? " + ((Rabbit) animal[i]).getZiekteGen() + "; alive?" + animal[i].isAlive());
	  			rabbitCount++;
	  		}
	  		
	  		else {
	  			System.out.println("Location " + i + ": No animal" );
	  		}
	  	}
	  	//There should be 0 rabbits, they shouldve died from the virus
	    assertEquals(0, rabbitCount);
	    
	    
	    
	}
	

}
